#!/usr/bin/env python3
"""Docstring for the sign_in_code_core.py module.

@author Juntong
@date   2022-10-13

签到二维码核心文件
"""
import os
import json
import socket
import tempfile
import threading
import socketserver

import yaml
import qrcode
from PIL import Image

try:
    from . import PROJECT_DIR, CONFIG_PATH
except ImportError:
    PROJECT_DIR = os.path.dirname(__file__)
    CONFIG_PATH = os.path.join(PROJECT_DIR, "config.yaml")


def gen_code(data) -> Image.Image:
    qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_H)
    qr.add_data(data)
    return qr.make_image()


def gen_code_to_temp_file(data, file_name=None, temp_directory=None) -> tuple[tempfile.TemporaryDirectory, str]:
    if temp_directory is None:
        temp_directory = tempfile.TemporaryDirectory()
    if file_name is None:
        file_name = "SignInQR-%d.jpg" % len(os.listdir(temp_directory.name))
    img = gen_code(data)
    img.save(os.path.join(temp_directory.name, file_name))
    return temp_directory, file_name


class CommunicationHandle(socketserver.BaseRequestHandler):
    """
    通信使用的Handle

    ---
    data:
    - 'command':
        - 'gen':
            + "code data":
                将生成的二维码数据
            <- 'path':
                返回二维码保存位置
        - 'code':
            <- 'path':
                返回二维码保存位置
            <- 'data':
                二维码的数据
        - 'close':
            关闭服务器
            <- 'message':
                True
    - <data>
    """

    qrcode_path = None
    qrcode_data = None
    going_shutdown = False
    code_temp_directory = None

    def handle(self):
        # self.request is the TCP socket connected to the client
        cls = type(self)
        keep = True and not cls.going_shutdown  # 若 shutdown 为真，则不再响应请求

        while keep:
            ret = {"error": True, "continue": False}

            data_text = self.request.recv(1024).strip()
            data = json.loads(data_text)
            addr = self.client_address[0]
            print(__name__, type(self).__name__, "get request from", addr.join("[]"), 'data ', data)
            cmd = data.get("command", "default_command")
            func = getattr(self, cmd)

            try:
                ret = func(data)
            finally:
                keep = ret.pop("continue")
            self.request.sendall(json.dumps(ret).encode("utf-8"))

        self.request.close()

        if self.going_shutdown:
            # 准备关闭会话
            self.server.server_close()
            thr = threading.Thread(target=self.server.shutdown)  # 我愿称之为邪门操作……
            thr.start()
            print(f"Server at {self.server.server_address} shutdown.")

    def gen(self, data: dict) -> dict:
        cls = type(self)
        cls.qrcode_data = data.get("code data")
        cls.code_temp_directory, file_name = gen_code_to_temp_file(cls.qrcode_data, None, cls.code_temp_directory)
        cls.qrcode_path = os.path.join(cls.code_temp_directory.name, file_name)
        return {"path": cls.qrcode_path, "continue": False}

    def code(self, data: dict) -> dict:
        cls = type(self)
        return {"path": cls.qrcode_path, "data": cls.qrcode_data, "continue": False}

    def close(self, data: dict) -> dict:
        cls = type(self)
        cls.going_shutdown = True
        return {"message": True, "continue": False}


def fast_start_server():
    """
    根据配置快速启动服务器
    Returns
    -------
    None
    """
    with open(CONFIG_PATH, "r", encoding="utf-8") as fp:
        config_data = yaml.safe_load(fp)
    addr = config_data["sign_in_server_address"]
    port = config_data["sign_in_server_port"]

    print(f'Start server at "{addr}:{port}"...')

    with socketserver.TCPServer((addr, port), CommunicationHandle) as server:
        server.serve_forever()


def fast_send(data):
    """
    根据配置快速发送数据致服务器
    Returns
    -------
    dict
    """
    with open(CONFIG_PATH, "r", encoding="utf-8") as fp:
        config_data = yaml.safe_load(fp)
    addr = config_data["sign_in_server_address"]
    port = config_data["sign_in_server_port"]

    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.connect((addr, port))
        sock.sendall(json.dumps(data).encode())
        received = str(sock.recv(1024), "utf-8")

    return json.loads(received)


if __name__ == "__main__":
    fast_start_server()
